Sending Events

Once you have created a metered feature, you can start transmitting events to Flexprice. Events are JSON payloads that describe usage activity and are transmitted via HTTP POST requests.

Prerequisites

  • A metered feature created in Flexprice
  • Your API key from the Admin Dashboard
  • The Event Name from your feature configuration

API Endpoints

Single Event

POST https://api.cloud.flexprice.io/v1/events

Bulk Events

POST https://api.cloud.flexprice.io/v1/events/bulk

Authentication

Include your API key in the request header:
x-api-key: <your_api_key>
Important: Keep your API key secure and never expose it in client-side code.

Event Payload Structure

Required Fields

FieldTypeDescriptionExample
event_namestringMust exactly match your feature’s Event Name"model.usage"
external_customer_idstringYour identifier for the customer"cust_123"

Conditional Fields

FieldTypeWhen RequiredDescription
propertiesobjectFor Sum, Max, Latest, Unique CountContains the values to aggregate
properties.<field>number/stringWhen aggregation field is setThe exact field name from your feature configuration

Optional Fields

FieldTypeDescriptionExample
event_idstringYour unique identifier for the event"evt_123"
timestampstringISO 8601 UTC timestamp"2025-08-22T07:05:49.441Z"
sourcestringOrigin of the event"api", "worker"

Single Event Examples

Basic Event (Count Aggregation)

If your feature uses Count aggregation, you only need the required fields:
{
  "event_name": "api.calls",
  "external_customer_id": "cust_123"
}

Event with Properties (Sum Aggregation)

If your feature uses Sum aggregation with field credits:
{
  "event_name": "model.usage",
  "external_customer_id": "cust_123",
  "properties": {
    "credits": 2
  }
}

Complete Event Example

{
  "event_name": "model.usage",
  "external_customer_id": "cust_123",
  "properties": {
    "credits": 2,
    "model": "gpt-4",
    "region": "us-east-1"
  },
  "event_id": "evt_abc123",
  "timestamp": "2025-08-22T07:05:49.441Z",
  "source": "api"
}

cURL Examples

Single Event

curl --request POST \
  --url https://api.cloud.flexprice.io/v1/events \
  --header 'Content-Type: application/json' \
  --header 'x-api-key: <your_api_key>' \
  --data '{
    "event_name": "model.usage",
    "external_customer_id": "cust-test-customer",
    "properties": {
      "credits": 2
    }
  }'
cURL Event Ingestion Example

Response

{
  "event_id": "event_01K389J4M1F1NZG6XP0AMD6J52",
  "message": "Event accepted for processing"
}
Postman Event Ingestion Example

Bulk Events

Transmit multiple events in a single request for better performance:
curl --request POST \
  --url https://api.cloud.flexprice.io/v1/events/bulk \
  --header 'Content-Type: application/json' \
  --header 'x-api-key: <your_api_key>' \
  --data '{
    "events": [
      {
        "event_name": "model.usage",
        "external_customer_id": "cust-test-customer",
        "properties": { "credits": 2 }
      },
      {
        "event_name": "model.usage",
        "external_customer_id": "cust-another-customer",
        "properties": { "credits": 5 }
      }
    ]
  }'

Language Examples

JavaScript (Node.js)

const fetch = require("node-fetch");

async function sendEvent() {
  const response = await fetch("https://api.cloud.flexprice.io/v1/events", {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
      "x-api-key": process.env.FLEXPRICE_API_KEY,
    },
    body: JSON.stringify({
      event_name: "model.usage",
      external_customer_id: "cust-test-customer",
      properties: { credits: 2 },
      source: "api",
    }),
  });

  const result = await response.json();
  console.log("Event transmitted:", result);
}

Python

import os
import requests
import json

def send_event():
    url = "https://api.cloud.flexprice.io/v1/events"
    headers = {
        "Content-Type": "application/json",
        "x-api-key": os.environ["FLEXPRICE_API_KEY"]
    }

    data = {
        "event_name": "model.usage",
        "external_customer_id": "cust-test-customer",
        "properties": {"credits": 2},
        "source": "api"
    }

    response = requests.post(url, headers=headers, json=data)
    result = response.json()
    print("Event transmitted:", result)

Go

package main

import (
    "bytes"
    "encoding/json"
    "fmt"
    "net/http"
    "os"
)

type Event struct {
    EventName           string                 `json:"event_name"`
    ExternalCustomerID  string                 `json:"external_customer_id"`
    Properties          map[string]interface{} `json:"properties,omitempty"`
    Source              string                 `json:"source,omitempty"`
}

func sendEvent() error {
    event := Event{
        EventName:          "model.usage",
        ExternalCustomerID: "cust-test-customer",
        Properties: map[string]interface{}{
            "credits": 2,
        },
        Source: "api",
    }

    jsonData, err := json.Marshal(event)
    if err != nil {
        return err
    }

    req, err := http.NewRequest("POST", "https://api.cloud.flexprice.io/v1/events", bytes.NewBuffer(jsonData))
    if err != nil {
        return err
    }

    req.Header.Set("Content-Type", "application/json")
    req.Header.Set("x-api-key", os.Getenv("FLEXPRICE_API_KEY"))

    client := &http.Client{}
    resp, err := client.Do(req)
    if err != nil {
        return err
    }
    defer resp.Body.Close()

    fmt.Printf("Event transmitted with status: %d\n", resp.StatusCode)
    return nil
}

PHP

<?php

function sendEvent() {
    $url = 'https://api.cloud.flexprice.io/v1/events';
    $apiKey = $_ENV['FLEXPRICE_API_KEY'];

    $data = [
        'event_name' => 'model.usage',
        'external_customer_id' => 'cust-test-customer',
        'properties' => [
            'credits' => 2
        ],
        'source' => 'api'
    ];

    $ch = curl_init();
    curl_setopt($ch, CURLOPT_URL, $url);
    curl_setopt($ch, CURLOPT_POST, true);
    curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data));
    curl_setopt($ch, CURLOPT_HTTPHEADER, [
        'Content-Type: application/json',
        'x-api-key: ' . $apiKey
    ]);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);

    $response = curl_exec($ch);
    $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
    curl_close($ch);

    echo "Event transmitted with status: $httpCode\n";
    echo "Response: $response\n";
}

Data Types and Best Practices

Property Values

  • Numbers: Use for Sum, Max aggregations
    "properties": { "credits": 2 }
    
  • Strings: Use for Unique Count, Latest aggregations
    "properties": { "user_id": "user_123" }
    

Timestamps

  • Use ISO 8601 format in UTC
  • Example: "2025-08-22T07:05:49.441Z"
  • If omitted, server time is used

Event IDs

  • Optional but recommended for traceability
  • Should be unique within your system
  • Helps with debugging and idempotency

Common Patterns

API Usage Tracking

{
  "event_name": "api.calls",
  "external_customer_id": "cust_123",
  "properties": {
    "endpoint": "/v1/chat",
    "method": "POST",
    "response_time": 150
  },
  "source": "api"
}

Storage Usage

{
  "event_name": "storage.usage",
  "external_customer_id": "cust_123",
  "properties": {
    "gb": 1.5,
    "bucket": "user-uploads"
  },
  "source": "storage-service"
}

User Activity

{
  "event_name": "user.login",
  "external_customer_id": "cust_123",
  "properties": {
    "user_id": "user_456",
    "platform": "web"
  },
  "source": "auth-service"
}

Error Handling

HTTP Status Codes

  • 202 Accepted: Event accepted for processing
  • 400 Bad Request: Invalid payload or missing required fields
  • 401 Unauthorized: Invalid or missing API key
  • 429 Too Many Requests: Rate limit exceeded

Common Errors

Missing required field
{
  "error": "Missing required field: event_name"
}
Invalid event_name
{
  "error": "Event name 'unknown.event' not found"
}
Invalid customer
{
  "error": "Customer with external_customer_id 'invalid_id' not found"
}

Rate Limits

  • Single events: 1000 requests per minute
  • Bulk events: 100 requests per minute (up to 1000 events per request)
  • Response: 429 status code when exceeded

Best Practices

1. Validate Your Configuration

Before transmitting production events:
  • Verify Event Name matches your feature exactly
  • Confirm Aggregation Field exists in your events
  • Test with a small number of events first

2. Handle Failures Gracefully

async function sendEventWithRetry(eventData, maxRetries = 3) {
  for (let attempt = 1; attempt <= maxRetries; attempt++) {
    try {
      const response = await fetch("https://api.cloud.flexprice.io/v1/events", {
        method: "POST",
        headers: {
          "Content-Type": "application/json",
          "x-api-key": process.env.FLEXPRICE_API_KEY,
        },
        body: JSON.stringify(eventData),
      });

      if (response.status === 202) {
        return await response.json();
      }

      if (response.status === 429) {
        // Rate limited — wait and retry
        await new Promise((resolve) => setTimeout(resolve, 1000 * attempt));
        continue;
      }

      throw new Error(`HTTP ${response.status}: ${await response.text()}`);
    } catch (error) {
      if (attempt === maxRetries) {
        throw error;
      }
      // Wait before retry
      await new Promise((resolve) => setTimeout(resolve, 1000 * attempt));
    }
  }
}

3. Use Bulk Events for High Volume

When transmitting many events, use the bulk endpoint:
const events = [
  { event_name: "api.calls", external_customer_id: "cust_1" },
  { event_name: "api.calls", external_customer_id: "cust_2" },
  // ... more events
];

const response = await fetch("https://api.cloud.flexprice.io/v1/events/bulk", {
  method: "POST",
  headers: {
    "Content-Type": "application/json",
    "x-api-key": process.env.FLEXPRICE_API_KEY,
  },
  body: JSON.stringify({ events }),
});

4. Include Relevant Metadata

Add useful properties to your events for debugging:
{
  "event_name": "model.usage",
  "external_customer_id": "cust_123",
  "properties": {
    "credits": 2,
    "request_id": "req_abc123",
    "version": "1.0"
  },
  "source": "api"
}

Next Steps

After sending events, you should:
  1. Validate Events - Verify that events are being processed correctly
  2. Connect to Billing - Set up pricing and subscriptions

Troubleshooting

”Event not found” error

  • Check that Event Name matches your feature exactly
  • Verify the feature exists and is active

”Customer not found” error

  • Ensure the customer exists in Flexprice
  • Check that external_customer_id matches exactly

”Missing required field” error

  • Verify all required fields are present
  • Check field names match exactly (case-sensitive)

Events not appearing in dashboard

  • Check the Event Debugger for processing status
  • Verify Event Name and Aggregation Field configuration
  • Ensure customer has an active subscription with the feature